using System.Collections.Generic;
using System.Linq;
using System;
using System.Reflection;
using System.Reflection.Emit;

namespace MyUtils.Investigate
{
	public sealed class MonoReflectionInstruction
	{

		int offset;
		OpCode opcode;
		object operand;

		MonoReflectionInstruction previous;
		MonoReflectionInstruction next;

		public int Offset
		{
			get { return offset; }
		}

		public OpCode OpCode
		{
			get { return opcode; }
		}

		public object Operand
		{
			get { return operand; }
			internal set { operand = value; }
		}

		public MonoReflectionInstruction Previous
		{
			get { return previous; }
			internal set { previous = value; }
		}

		public MonoReflectionInstruction Next
		{
			get { return next; }
			internal set { next = value; }
		}

		internal MonoReflectionInstruction(int offset, OpCode opcode)
		{
			this.offset = offset;
			this.opcode = opcode;
		}

		public int GetSize()
		{
			int size = opcode.Size;

			switch (opcode.OperandType)
			{
				case OperandType.InlineSwitch:
					size += (1 + ((int[])operand).Length) * 4;
					break;
				case OperandType.InlineI8:
				case OperandType.InlineR:
					size += 8;
					break;
				case OperandType.InlineBrTarget:
				case OperandType.InlineField:
				case OperandType.InlineI:
				case OperandType.InlineMethod:
				case OperandType.InlineString:
				case OperandType.InlineTok:
				case OperandType.InlineType:
				case OperandType.ShortInlineR:
					size += 4;
					break;
				case OperandType.InlineVar:
					size += 2;
					break;
				case OperandType.ShortInlineBrTarget:
				case OperandType.ShortInlineI:
				case OperandType.ShortInlineVar:
					size += 1;
					break;
			}

			return size;
		}

		public override string ToString()
		{
			if (operand == null)
			{
				return opcode.Name;
			}
			else
			{
				return string.Format("{0} {1}", opcode.Name, OperandToString);
			}
		}

		string OperandToString
		{
			get
			{
				var mi = operand as MemberInfo;
				if (mi != null)
				{
					return string.Format("{0} {1}", mi.DeclaringType.Name, mi);
				}
				return string.Format("{0}", operand);
			}
		}
	}
}